import time
from dataclasses import dataclass
from enum import IntEnum
import struct
from common import Crc16_Cal_Fun
from AGV_F50A_Descript import *


class E_FrameHead(IntEnum):
    """
    tcp交互协议帧的头header
    """

    Host_Id: int = 0x02
    Car_Id: int = 0xAA
    Car_Cmd_Id: int = 0xAB
    TcpServer_Id: int = 0xBB


class E_FrameCmd(IntEnum):
    """
    tcpServer 发送帧中的 命令字
    """

    UpdatePath: int = 0x01
    SignalCmd: int = 0x02
    HeartBeat: int = 0x03
    Activate: int = 0x80
    RespLogin: int = 0xF0


class E_DeviceCmd(IntEnum):
    """
    设备数据帧中的 命令字
    """

    RealtimePackage: int = 0x1
    HeartBeat: int = 0x2
    PeriodicReport: int = 0x3
    FaultPackage: int = 0x04
    SignalCmd_Resp: int = 0x06
    StateCompleteResp: int = 0x09
    Login: int = 0x10
    QRCodePath_Act_Resp_NG: int = 0xA0
    QRCodePath_Act_Resp_OK: int = 0xA1


class E_PathAction_DataType(IntEnum):
    """
    命令码/动作码: 路径包 数据类型
    """

    ShortType: int = 0x00
    IntType: int = 0x02


class E_PathAction(IntEnum):
    """
    命令码/动作码: 路径包
    """

    Stop: int = 0x0
    LineMove: int = 0x05
    WheelSteering: int = 0x10
    PoseState_Move_Up: int = 0x18
    PoseState_Move_Back: int = 0x19
    Lifting_UpDown: int = 0x20
    Charger: int = 0x30
    Discharger: int = 0x3F


class E_SignalCmd(IntEnum):
    """
    命令码/动作码: 命令包
    """

    Move_Line: int = 0x02  # 直行
    WheelSteering: int = 0x10  # 转弯
    Lift_Up: int = 0x20
    EntrySleep: int = 0x40
    ExitSleep: int = 0x41
    InSitu_calibration: int = 0x58  # 原地校准, 参数偏移值 5~30mm
    Rotation_calibration: int = 0x59  # 旋转标定
    Pause: int = 0xF0
    Resume: int = 0xF3
    Cancel: int = 0xF4


@dataclass
class Frame:
    """
    tcp帧 协议格式
    (格式: filed len  type description)
        header 1B,uint8_t       头标识
        payload_len 2B,uint16_t 数据长度client_id + timestamp + cmd_code + data
        client_id 4B,uint32_t   agv SN码
        timestamp 4B,uint32_t   时间戳
        cmd_code 1B,uint8_t     命令字
        data nb,uint8_t         数据
        crc16 2B,uint16_t       crc16 modbus校验码
    """

    header: int = 0xBB
    payload_len: int = 0
    client_id: int = 0
    timestamp: int = 0
    cmd_code: int = 0
    data: bytes = b""
    crc16: int = 0

    def to_bytes(self) -> bytes:
        """
        组包: 封装agv tcp协议帧
        """
        self.timestamp = int(time.time())
        self.payload_len = 9 + len(self.data)
        data = struct.pack(
            f"<IIB{len(self.data)}s",
            self.client_id,
            self.timestamp,
            self.cmd_code,
            self.data,
        )
        data = struct.pack("<H", self.payload_len) + data
        self.crc16 = Crc16_Cal_Fun(data)

        raw_bytes = struct.pack(
            f"<BHIIB{len(self.data)}sH",
            self.header,
            self.payload_len,
            self.client_id,
            self.timestamp,
            self.cmd_code,
            self.data,
            self.crc16,
        )
        return raw_bytes

    def from_bytes(self, data: bytes):
        """
        解包: 解析agv tcp协议帧
        """
        # 1.校验head
        if not data[0] in (E_FrameHead.Car_Id, E_FrameHead.Car_Cmd_Id):
            print(f"header校验失败, 丢弃 head:{data[0]:02x}")
            return None

        # 2.校验长度
        payload_len = struct.unpack_from("<H", data[1:3])[0]
        if payload_len != len(data) - 5:
            print("长度校验失败, 丢弃")
            return None
        data_len = payload_len - 9

        # 3.校验crc16
        cal_crc16 = Crc16_Cal_Fun(data[1:-2])
        data_crc16 = struct.unpack_from("<H", data[-2:])[0]
        if cal_crc16 != data_crc16:
            print("crc16校验失败, 丢弃")
            return None

        # 4.解析报文
        (
            self.header,
            self.payload_len,
            self.client_id,
            self.timestamp,
            self.cmd_code,
            self.data,
        ) = struct.unpack_from(f"<BHIIB{data_len}s", data)
        return self


@dataclass
class QRCodePath:
    """
    二维码路径格式
    """

    address: int
    cmd: int
    type: int
    data: int


@dataclass
class DMAddress:
    """
    二维码(Data Matrix)地址信息: id + x,y,z偏移
    """

    id: int = 0
    offset_x: int = 0
    offset_y: int = 0
    offset_z: float = 0


@dataclass
class FaultFrame:
    """
    故障码数据
    """

    id: int = 0
    stat: int = 0
    data1: int = 0
    data2: int = 0
    used: int = 0
    cur_stat: int = 0

    def unpack(self, data: bytes):
        """
        解析数据,字段
        """
        (
            self.id,
            self.stat,
            self.data1,
            self.data2,
            self.used,
            self.cur_stat,
        ) = struct.unpack_from("<HBIIBB", data)
        return self

    def __str__(self):
        fault_code_str = Fault_Code_Description.get(self.id, "未知故障码")
        cur_stat_str = CurStat_Description.get(self.cur_stat, "未知状态")
        return f"fault故障码| code:{self.id:04X}={fault_code_str}, stat:{self.stat:02X}, {self.data1:0X}, {self.data2:0X}; stat:{self.cur_stat:02X}:{cur_stat_str}"


@dataclass
class AgvPeriodicState:
    """
    周期上报数据
    """

    battery_soc: int = 0
    left_motor_temp: int = 0
    right_motor_temp: int = 0
    rotary_motor_temp: int = 0
    lift_motor_temp: int = 0
    battery_temp: int = 0
    battery_fault: int = 0
    battery_voltage: int = 0
    res: int = 0
    uptime: int = 0
    # 惯导角度 单位/度
    inertial_navigat_angle: float = 0.0
    last_QRCode_offset: float = 0.0
    left_motor_encode: int = 0
    right_motor_encode: int = 0
    sensor_state: int = 0
    battery_current: int = 0
    total_running_time: int = 0
    charge_number: int = 0
    used_flag: int = 0
    cur_stat: int = 0

    def unpack(self, data: bytes):
        """
        解析数据
        """
        (
            self.battery_soc,
            self.left_motor_temp,
            self.right_motor_temp,
            self.rotary_motor_temp,
            self.lift_motor_temp,
            self.battery_temp,
            self.battery_fault,
            self.battery_voltage,
            self.res,
            self.uptime,
            self.inertial_navigat_angle,
            self.last_QRCode_offset,
            self.left_motor_encode,
            self.right_motor_encode,
            self.sensor_state,
            self.battery_current,
            self.total_running_time,
            self.charge_number,
            self.used_flag,
            self.cur_stat,
        ) = struct.unpack_from("<HbbbbbBHIiffiiIiiiBB", data)
        return self

    def __str__(self):
        cur_stat_str = CurStat_Description.get(self.cur_stat, "未知状态")
        return f"period周期包| battery:soc:{self.battery_soc} vol:{self.battery_voltage} cur:{self.battery_current}; used:{self.used_flag} stat:{self.cur_stat:02x}={cur_stat_str}"


@dataclass
class AgvRealtimeState:
    """
    实时状态报文数据
    """

    agv_address: DMAddress = DMAddress()  # 二维码地址
    material_num: int = 0  # 物料编号
    res: int = 0  # 保留
    used_flag: int = 0  # 物料使用标志位
    cur_stat: int = 0  # 当前状态

    def unpack(self, data: bytes):
        """
        解析数据
        """
        (
            self.agv_address.id,
            self.agv_address.offset_x,
            self.agv_address.offset_y,
            self.agv_address.offset_z,
            self.material_num,
            self.res,
            self.used_flag,
            self.cur_stat,
        ) = struct.unpack_from("<IhhfQIBB", data)
        return self

    def __str__(self):
        cur_stat_str = CurStat_Description.get(self.cur_stat, "未知状态")
        return f"realtime实时包| address:{self.agv_address.id} offset: x:{self.agv_address.offset_x} y:{self.agv_address.offset_y} z:{round(self.agv_address.offset_z,1)} used:{self.used_flag} stat:{self.cur_stat:02X}={cur_stat_str}"


@dataclass
class LoinFrame:
    """
    登陆报文数据
    """

    build_time: int = 0
    device_type: int = 0
    hw_version: int = 0
    product_data: int = 0
    comm_version: int = 0
    last_repair_time: int = 0
    battery_soc: int = 0
    agv_address: DMAddress = DMAddress()
    material_str_num: bytes = b""
    res: int = 0
    used_flag: int = 0
    cur_state: int = 0
    fault_code: int = 0
    signal_quality: int = 0
    model: int = 0
    sw_version: int = 0
    publish_time: int = 0
    battery_type: int = 0
    max_charge_current: int = 0

    def unpack(self, data: bytes):
        (
            self.build_time,
            self.device_type,
            self.hw_version,
            self.product_data,
            self.comm_version,
            # ----
            self.last_repair_time,
            self.battery_soc,
            self.agv_address.id,
            self.agv_address.offset_x,
            self.agv_address.offset_y,
            self.agv_address.offset_z,
            # ----
            self.material_str_num,
            self.res,
            self.used_flag,
            self.cur_stat,
            self.fault_code,
            # ---
            self.signal_quality,
            self.model,
            self.sw_version,
            self.publish_time,
            self.battery_type,
            # ---
            self.max_charge_current,
        ) = struct.unpack_from("<IBHIIIHIHHf12sIBBHBHIIBH", data)
        return self

    def __str__(self):
        if self.fault_code == 0x0:
            fault_code_str = "无故障"
        else:
            fault_code_str = Fault_Code_Description.get(self.id, "未知故障码")
        cur_stat_str = CurStat_Description.get(self.cur_stat, "未知状态")
        return f"loginFrame登陆包| 电量:{self.battery_soc}% address:{self.agv_address.id} cur_stat:{self.cur_state}={cur_stat_str} fault_code:{self.fault_code}={fault_code_str}"


@dataclass
class HeartBeatFrame:
    """
    心跳报文
    """

    address_id: int = 0
    speed: int = 0
    base_id: int = 0
    base_offset_x: float = 0
    base_offset_y: float = 0
    base_offset_z: float = 0

    def unpack(self, data: bytes):
        (
            self.address_id,
            self.speed,
            self.base_id,
            self.base_offset_x,
            self.base_offset_y,
            self.base_offset_z,
        ) = struct.unpack_from("<IiIfff", data)
        return self

    def __str__(self):
        return f"HeartBeat心跳包| address:{self.address_id} speed:{self.speed}mm/s base:{self.base_id} x:{round(self.base_offset_x,1)} y:{round(self.base_offset_y,1)} z:{round(self.base_offset_z,1)}"


@dataclass
class StateCompleteFrame:
    """
    动作完成
    """

    complete_act_type: int = 0
    address_id: int = 0
    angle: int = 0
    res1: int = 0
    res2: int = 0
    cur_stat: int = 0
    used_flag: int = 0
    act_code: int = 0

    def unpack(self, data: bytes):
        (
            self.complete_act_type,
            self.address_id,
            self.angle,
            self.res1,
            self.res2,
            self.cur_stat,
            self.used_flag,
            self.act_code,
        ) = struct.unpack_from("<BIHIHBBB", data)
        return self

    def __str__(self):
        actType_str = StateComple_Type_Description.get(
            self.complete_act_type, "未知动作类型"
        )
        return f"stateComplete动作完成包| address:{self.address_id} angle:{self.angle} act: type:{self.complete_act_type:02X}={actType_str} code:{self.act_code:02X}"


@dataclass
class AgvClient:
    """
    agv设备登陆 tcpServer, 保存信息
    """

    client_id: int = 0
    socket: any = None
    login_frame: LoinFrame = LoinFrame()
    fault_frame: FaultFrame = FaultFrame()
    periodic_state: AgvPeriodicState = AgvPeriodicState()
    realtime_state: AgvRealtimeState = AgvRealtimeState()
